/*
 * QTXpost - a Qt-based blog client.
 * Copyright (C) 2010 Helen Sommer
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; version 3 of the License.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program (in COPYING); if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 * MA  02111-1307  USA
 *
 * The project is hosted on http://code.google.com/p/qtxpost
 */

#include "Config.h"
#include "MainWindow.h"

Config::Config(QString dir, QWidget *parent) 
  : QDialog(parent)
{
  setupUi(this);

  connect(cancelButton, SIGNAL(clicked()), this, SLOT(close()));
  connect(saveButton, SIGNAL(clicked()), this, SLOT(saveConfig()));
  connect(sqliteToggle, SIGNAL(toggled(bool)), this, SLOT(databaseChanged(bool)));
  connect(mysqlToggle, SIGNAL(toggled(bool)), this, SLOT(databaseChanged(bool)));

  securityCombo->addItem("public");
  securityCombo->addItem("friends");
  securityCombo->addItem("private");
  securityCombo->addItem("custom");

  configDirectory = (dir == "") ? ".qtxpost" : dir;

  QRegExp rx("^(~|\\.\\.|\\.\\/)");
  if (configDirectory.contains(rx)) {
    qDebug() << "Invalid config directory " << configDirectory << "! Please enter either a full pathname or a name relative to the home directory.";
    exit(0);
  }

  if (!configDirectory.startsWith("/")) {
    configDirectory.prepend("/");
    configDirectory.prepend(QDir::homePath());
  }

  configFilename = "qtxpost.conf";
  tables << 
    "communities" << 
    "groups" << 
    "default_icons" << 
    "icons" << 
    "keywords" << 
    "moods" <<
    "users" <<
    "friends";
  
  setDefaults();
  bool configExists = readConfig();
  initializeGui();
  if (!configExists) 
    saveConfig();
  if (!initializeDb()) {
    tabs->setCurrentIndex(1);
    error = true;
  }
  else {
    tabs->setCurrentIndex(0);
    error = false;
  }
}

void Config::setDefaults()
{
  //should be in an installation script?

  settingsValues["db"] = QStringList() << "sqlite" << "mysql";
  settings["db"] = "sqlite";
  settings["dbhost"] = "localhost";
  settings["dbname"] = "qtxpost";
  settings["dbuser"] = "";
  settings["dbpass"] = "";

  settingsValues["show_toolbar"] = QStringList() << "0" << "1";
  settings["show_toolbar"] = 1;

  settingsValues["show_music"] = QStringList() << "0" << "1";
  settings["show_music"] = 0;

  settingsValues["show_location"] = QStringList() << "0" << "1";
  settings["show_location"] = 0;

  settingsValues["show_tags"] = QStringList() << "0" << "1";
  settings["show_tags"] = 1;

  settingsValues["crossposting"] = QStringList() << "0" << "1";
  settings["crossposting"] = 1;

  settingsValues["save_communities_crossposting"] = QStringList() << "0" << "1";
  settings["save_communities_crossposting"] = 0;

  settingsValues["security"] = QStringList() << "public" << "friends" << "private" << "custom";
  settings["security"] = "public";

  settingsValues["save_custom_groups"] = QStringList() << "0" << "1";
  settings["save_custom_groups"] = 0;

  settingsValues["add_signature"] = QStringList() << "0" << "1";
  settings["add_signature"] = 0;

  settingsValues["shortcut_quit"] = QStringList() << "0" << "1";
  settings["shortcut_quit"] = 0;

  settings["signature"] = "";
}

bool Config::readConfig()
{
  QString key, value;
  
  QString name = QString("%1/%2").arg(configDirectory).arg(configFilename);
  QFile file(name);
  if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
    qDebug() << "Warning: can't open file " << name;
    return false;
  }
  QTextStream in(&file);
  bool continueLine = false;
  while (!in.atEnd()) {
    QString line = in.readLine().simplified();
    if (continueLine) {
      value.append(line);
      if (!line.endsWith("\\")) {
        continueLine = false;
        value.replace("\\","\n");
        addSetting(key, value);
      }
      continue;
    }
    if (line == "" || line.startsWith("#") || line.contains("^\\s*$")) continue;
    QStringList list = line.split(QRegExp("\\s*=\\s*"));
    key = list.at(0);
    if (list.size() == 2) {
      value = list.at(1);
    }
    else if (list.size() == 1) {
      qDebug() << "Warning: invalid string in config: " << line;
    }
    else { 
      // QRegExp can't split into only 2 parts?
      // so if there's "=" in the value, have to join the rest together
      QStringList values = list;
      values.removeFirst();
      value = values.join("=");
    }
    if (value.endsWith("\\")) {
      continueLine = true;
    }
    else {
      addSetting(key, value);
    }
  }

  if (continueLine && (key != "")) { // just in case if the last backslash was lost
    value.replace("\\","\n");
    addSetting(key, value);
  }
  file.close();
  return true;
}

void Config::addSetting(QString key, QString value)
{
  if (settings.contains(key)) {
    if (settingsValues.contains(key) && !settingsValues.value(key).contains(value)) {
      qDebug() << "Config error: Invalid value '" << value << "' for " << key <<"!";
    }
    else { 
      settings.insert(key,value);
    }
  }
}

void Config::initializeGui()
{
  toolbarToggle->setChecked(settings.value("show_toolbar").toBool());
  musicToggle->setChecked(settings.value("show_music").toBool());
  locationToggle->setChecked(settings.value("show_location").toBool());
  tagsToggle->setChecked(settings.value("show_tags").toBool());
  wordcountToggle->setChecked(settings.value("show_wordcount").toBool());
  crosspostingToggle->setChecked(settings.value("crossposting").toBool());
  saveCommunitiesCrosspostingToggle->setChecked(settings.value("save_communities_crossposting").toBool());
  saveGroupsToggle->setChecked(settings.value("save_custom_groups").toBool());
  securityCombo->setCurrentIndex(securityCombo->findText(settings.value("security").toString()));
  signatureToggle->setChecked(settings.value("add_signature").toBool());
  signatureText->setPlainText(settings.value("signature").toString());
  shortcutQuitToggle->setChecked(settings.value("shortcut_quit").toBool());

  dbhostLine->setText(settings.value("dbhost").toString());
  dbnameLine->setText(settings.value("dbname").toString());
  dbuserLine->setText(settings.value("dbuser").toString());
  dbpassLine->setText(settings.value("dbpass").toString());

  if (settings.value("db").toString() == "sqlite") {
    sqliteToggle->setChecked(true);
    mysqlToggle->setChecked(false);
  }
  else if (settings.value("db").toString() == "mysql") {
    sqliteToggle->setChecked(false);
    mysqlToggle->setChecked(true);
  }
}

bool Config::saveConfig()
{
  QString currentDb = settings.value("db").toString();
  QString currentHost = settings.value("dbhost").toString();
  QString currentName = settings.value("dbname").toString();
  QString currentUser = settings.value("dbuser").toString();
  QString currentPass = settings.value("dbpass").toString();

  settings.insert("show_toolbar", QVariant(toolbarToggle->isChecked()).toInt());
  settings.insert("show_music", QVariant(musicToggle->isChecked()).toInt());
  settings.insert("show_location", QVariant(locationToggle->isChecked()).toInt());
  settings.insert("show_tags", QVariant(tagsToggle->isChecked()).toInt());
  settings.insert("show_wordcount", QVariant(wordcountToggle->isChecked()).toInt());
  settings.insert("crossposting", QVariant(crosspostingToggle->isChecked()).toInt());
  settings.insert("save_communities_crossposting", QVariant(saveCommunitiesCrosspostingToggle->isChecked()).toInt());
  settings.insert("save_custom_groups", QVariant(saveGroupsToggle->isChecked()).toInt());
  settings.insert("add_signature", QVariant(signatureToggle->isChecked()).toInt());
  settings.insert("shortcut_quit", QVariant(shortcutQuitToggle->isChecked()).toInt());
  settings.insert("security", securityCombo->currentText());
  settings.insert("signature", signatureText->toPlainText());

  settings.insert("db", (mysqlToggle->isChecked()) ? "mysql" : (sqliteToggle->isChecked()) ? "sqlite" : "");
  settings.insert("dbhost", dbhostLine->text());
  settings.insert("dbname", dbnameLine->text());
  settings.insert("dbuser", dbuserLine->text());
  settings.insert("dbpass", dbpassLine->text());

  QDir dir(configDirectory);
  if (!dir.exists(configDirectory)) {
    if (!dir.mkpath(configDirectory)) {
      qDebug() << "Error: can't create config directory " << configDirectory;
      exit(1);
    }
    else {
      qDebug() << "Creating config directory" << configDirectory;
    }
  }
  
  QString name = QString("%1/%2").arg(configDirectory).arg(configFilename);
  QFile file(name);
  if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
    qDebug() << "Can't open file " << name << " for writing";
    exit(1);
  }
  file.setPermissions(QFile::ReadOwner|QFile::WriteOwner);
  QTextStream out(&file);
  QMapIterator<QString, QVariant> i(settings);
  while (i.hasNext()) {
    i.next();
    QString value = i.value().toString();
    value.replace("\n","\\\n");
    out << i.key() << " = " << value << "\n";
  }
  file.close();
  close();

  bool restart = false;
  if (settings.value("db").toString() != currentDb) 
    restart = true;
  else if (settings.value("db").toString() == "mysql") {
    if (settings.value("dbhost").toString() != currentHost ||
        settings.value("dbname").toString() != currentName ||
        settings.value("dbuser").toString() != currentUser ||
        settings.value("dbpass").toString() != currentPass) {
      restart = true;
    }
  }
  if (restart)
    restartMain();
  
  emit updated();
  return true;
}

void Config::restartMain()
{
  extern QApplication *app;
  extern MainWindow *root;

  if (db.isOpen()) 
    db.close();
  if (!initializeDb())
    exit(1);
  
  if (root != 0) {
    disconnect (root, 0, 0, 0);
    delete root;
    root = 0;
  }
  root = new MainWindow();
  app->connect(app, SIGNAL(lastWindowClosed()), root, SLOT(close())); 
  root->show();
}

void Config::databaseChanged(bool)
{
  if (mysqlToggle->isChecked()) {
    dbhostLine->setEnabled(true);
    dbnameLine->setEnabled(true);
    dbuserLine->setEnabled(true);
    dbpassLine->setEnabled(true);
  }
  else {      
    dbhostLine->setEnabled(false);
    dbnameLine->setEnabled(false);
    dbuserLine->setEnabled(false);
    dbpassLine->setEnabled(false);
  }
}

bool Config::initializeDb()
{
  QString dbType = settings.value("db").toString();
  QString name;

  if (dbType == "mysql") {
    db = QSqlDatabase::addDatabase("QMYSQL");
    db.setHostName(settings.value("dbhost").toString());
    db.setDatabaseName(settings.value("dbname").toString());
    db.setUserName(settings.value("dbuser").toString());
    db.setPassword(settings.value("dbpass").toString());
    name = QString("%1 on %2").arg(settings.value("dbname").toString()).arg(settings.value("dbhost").toString());
  }
  else if (dbType == "sqlite") {
    db = QSqlDatabase::addDatabase("QSQLITE");
    name = QString("%1/qtxpost.db").arg(configDirectory);
    db.setDatabaseName(name);
    QFile file(name);
    file.setPermissions(QFile::ReadOwner|QFile::WriteOwner);
  }
  else {
    qDebug() << "Error: invalid database type " << dbType << " in configuration file.";
    exit(1);
  }

  if (!db.open()) {
    qDebug() << "Error: can't open" << dbType << "database" << name;
    if (dbType == "sqlite")
      exit(1);
    else {
      QMessageBox msgBox;
      msgBox.setIcon(QMessageBox::Warning);
      msgBox.setText(QString("Can't open %1 database %2 - please check your settings.").arg(dbType).arg(name));
      msgBox.exec();
      return false;
    }
  }

  QStringList tablesDb = db.tables();
  bool missing = false;
  for (int i = 0; i < tables.size(); i++) {
    if (!tablesDb.contains(tables.at(i))) {
      missing = true;
      break;
    }
  }
  
  if (missing) { // some/all tables are missing - recreate the DB
    // existing tables are not overwritten - the command silently fails
    QString schema = QString(":/sql/%1.schema").arg(dbType);
    QFile file(schema);
    if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
      qDebug() << "Error: can't open resource " << schema;
      exit(1);
    }
    QSqlQuery query;    
    QTextStream in(&file);
    while (!in.atEnd()) {
      QString command = in.readLine();
      if (!query.exec(command)) {
        if (!query.lastError().databaseText().contains("already exists")) {
          qDebug() << query.lastError();
        }
      }
    }
  }
  
  // fixing the table from 0.2.* - one-time 
  QString command = "select * from users limit 0,0";
  QSqlQuery query;
  if (!query.exec(command)) { // get a list of columns
    qDebug() << query.lastError();
  }
  else {
    QSqlRecord record = query.record();
    if (!record.contains("type")) {
      QList<QString> commands;
      if (dbType == "sqlite") {
        commands << "alter table users add column type integer unsigned default 0" << 
          "create index users_type on users(type)";
      }
      else if (dbType == "mysql") {
        commands << "alter table users add column type tinyint(1) unsigned default 0" <<
          "alter table users add index(type)";
      }
      executeList(commands);
    }
  }

  // fixing the table from 0.3.1*
  command = "select * from groups limit 0,0";
  if (!query.exec(command)) { // get a list of columns
    qDebug() << query.lastError();
  }
  else {
    QSqlRecord record = query.record();

    if (!record.contains("type")) {
      QList<QString> commands;
      if (dbType == "sqlite") {
        commands << "alter table groups add column type integer unsigned default 0" << 
          "create index groups_type on groups(type)";
      }
      else if (dbType == "mysql") {
        commands << "alter table groups add column type tinyint(1) unsigned default 0" <<
          "alter table groups add index(type)";
      }
      executeList(commands);
    }

    if (!record.contains("public")) {
      QList<QString> commands;
      if (dbType == "sqlite") {
        commands << "alter table groups add column public integer unsigned default 0" ;
      }
      else if (dbType == "mysql") {
        commands << "alter table groups add column public tinyint(1) unsigned default 0" ;
      }
      executeList(commands);
    }


  }

  
  return true;
}

void Config::executeList(QList<QString> commands)
{
  QSqlQuery query;

  for (int i = 0; i < commands.size(); i++) {
    qDebug() << commands.at(i);
    if (!query.exec(commands.at(i))) {
      qDebug() << "error = " << query.lastError();
    }
  }
}
